﻿using System;

namespace BrightSword.Feber.Core
{
    ///<summary>
    ///  This is a set of builder methods, exposed as a set of extension methods on the <see cref="Type" /> class. These builder methods generate methods and functions for in building Feber operation classes. See the examples for more details.
    ///</summary>
    public static class ObjectFunctionBuilder
    {
#if OLD

    /// <summary>
    ///   Use this function when you want to reflect over all the properties over a type and perform an operation on each property.
    /// </summary>
    /// <typeparam name="TInstance"> The (interface) type of the object being operated upon. Typically, <paramref
    ///    name="propertySetType" /> and <typeparamref name="TInstance" /> are isomorphic types, if not the same type </typeparam>
    /// <param name="propertySetType"> The type of the object on which to reflect properties </param>
    /// <param name="operationExpressionFactory"> A factory delegate which should return the appropriate expression for a given property with reference to the parameter </param>
    /// <param name="propertyFilter"> An optional filter to define a subset of properties to work on </param>
    /// <returns> A method which takes one argument of <typeparamref name="TInstance" /> and performs the specified operations on it </returns>
    /// <example>
    ///   Object Printing: For code which prints out an object's properties, we might want to write code as follows 
    ///  <code>
    ///  void Print(Foo instance)
    ///  {
    ///      Console.WriteLine("{0} (instance)", typeof (T).Name);
    ///      Console.WriteLine("{");
    ///      Console.WriteLine("\tHeight : {0}", instance.Height);
    ///      Console.WriteLine("\tWidth : {0}", instance.Width);
    ///      ...
    ///      Console.WriteLine("}");
    ///  }
    ///  </code>
    ///  This is obviously onerous, because one has to write out all the properties by hand.
    ///  Furthermore, it requires maintenance when the list of properties defined on Foo changes.
    ///  Instead, we could use this builder to generate us exactly such a function as follows: 
    ///  <code>
    ///  static class Printer{T}
    ///  {
    ///      private static readonly Action{T} _print = typeof (T).BuildCompositeAction{T}(
    ///          (_pi, _arg) => Expression.Call(
    ///              typeof (Console),
    ///              "WriteLine",
    ///              null,
    ///              Expression.Constant("\t{0} : {1}", typeof (string)),
    ///              Expression.Constant(_pi.Name, typeof (string)),
    ///              Expression.Call(typeof (Convert), "ToString", null, Expression.Property(_arg, _pi))));
    ///      
    ///      public static void Print(T instance)
    ///      {
    ///          Console.WriteLine("{0} (instance)", typeof (T).Name);
    ///          Console.WriteLine("{");
    ///          
    ///          _print(instance);
    /// 
    ///          Console.WriteLine("}");
    ///      }
    /// }
    ///  </code>
    ///  This dynamically generates the operation lambdas just once at the static initializer time, 
    ///  and allows the compiler to memoize the functions based on the generic type argument.
    ///  Consequently there is very little loss of runtime performance when Print() is called many times repetitively.
    /// </example>
        public static Action<TInstance> BuildCompositeAction<TInstance>(this Type propertySetType,
                                                                        Func<PropertyInfo, ParameterExpression, Expression> operationExpressionFactory,
                                                                        Func<PropertyInfo, bool> propertyFilter = null)
        {
            // if no filter is specified, use all properties
            propertyFilter = propertyFilter ?? (_ => true);

            // we are building a function of the form
            // void <anonymous>(TInstance _instance)
            // {
            //      <op expr>(_instance.<property 1>);
            //      <op expr>(_instance.<property 2>);
            //      ...
            //      <op expr>(_instance.<property N>);
            // }

            // create the [_instance] parameter
            var instanceParameterExpression = Expression.Parameter(typeof(TInstance),
                                                                   "_instance");

            // get the set of operation expressions for the set of properties
            var expressions = propertySetType.GetOperationExpressions(propertyFilter,
                                                                      operationExpressionFactory,
                                                                      instanceParameterExpression);

            // create a block expression surrounding all these expressions
            var blockExpression = Expression.Block(expressions);

            // create a lambda expression, wiring up the _instance parameter
            var lambdaExpression = Expression.Lambda<Action<TInstance>>(blockExpression,
                                                                        instanceParameterExpression);

            // voila!
            return lambdaExpression.Compile();
        }
        
#endif

#if OLD
		
    /// <summary>
    ///   Use this function when you want to reflect over all the properties over a type, compute some result for each property, and aggregate those results into a single value.
    /// </summary>
    /// <typeparam name="TInstance"> The (interface) type of the object being operated upon. Typically, <paramref
    ///    name="propertySetType" /> and <typeparamref name="TInstance" /> are isomorphic types, if not the same type </typeparam>
    /// <typeparam name="TResult"> The type of the result of the generated function, which is also the type of the result of the individual lambda expressions inside the generated function </typeparam>
    /// <param name="propertySetType"> The type of the object on which to reflect properties </param>
    /// <param name="operationExpressionFactory"> A factory delegate which should return the appropriate expression for a given property with reference to the parameter </param>
    /// <param name="conjunction"> A function which folds the set of results of the individual lambda expressions inside the generated function into a single value </param>
    /// <param name="seed"> The initial (default) result value of the fold </param>
    /// <param name="propertyFilter"> An optional filter to define a subset of properties to work on </param>
    /// <returns> A function which takes one argument of type <typeparamref name="TInstance" /> , computes a value for each property using the specified operation, and folds those values into a single result of <typeparamref
    ///    name="TResult" /> </returns>
    /// <example>
    ///   Simple Serialization: For code which serializes out all of an object's simple properties, we might want to write code as follows 
    ///  <code>string Serialize(Foo instance)
    ///  {
    ///      return String.Format("\tHeight : {0}", instance.Height) +
    ///              String.Format("\tWidth : {0}", instance.Width) + 
    ///              ...
    ///              ;
    ///  }</code>
    ///  This is obviously onerous, because one has to write out all the properties by hand.
    ///  Furthermore, it requires maintenance when the list of properties defined on Foo changes.
    ///  Instead, we could use this builder to generate us exactly such a function as follows:
    ///  <code>static class Serializer{T}
    ///  {
    ///      private static readonly Func{T, string} _serialize = typeof (T).BuildCompositeFunc{T, string}(
    ///          (_pi, _param) =>
    ///              Expression.Call(
    ///                  typeof (String),
    ///                  "Format",
    ///                  null,
    ///                  Expression.Constant("{0}:{1},", typeof (string)),
    ///                  Expression.Constant(_pi.Name, typeof (string)),
    ///                  Expression.Call(Expression.Property(_param, _pi), "ToString", null)), (_l, _r) => Expression.Call(_concat, _l, _r),
    ///                  Expression.Constant(""));
    /// 
    ///      public static string Serialize(T instance)
    ///      {
    ///          return String.Format("{{{0}}}", _toPrettyPrintedString(instance));
    ///      }
    ///  }</code> 
    ///  This dynamically generates the operation lambdas just once at the static initializer time, 
    ///  and allows the compiler to memoize the functions based on the generic type argument.
    ///  Consequently there is very little loss of runtime performance when Serialize() is called many times repetitively. 
    ///  The 
    ///  <code>Expression.Call(Expression.Property(_param, _pi), "ToString", null)), (_l, _r) => Expression.Call(_concat, _l, _r),</code>
    ///  expression is an example of a non-trivial accumulator function, which effectively calls results in code that looks like: 
    ///  <code>String.Concat(
    ///      String.Concat(
    ///          String.Concat(
    ///              "",
    ///              _instance.property1),
    ///          _instance.property2),
    ///     _instance.property3);</code>
    ///  More efficient ways are possible by simply improving on the quality of the expressions and factories used as the parameters to this function.
    /// </example>
        public static Func<TInstance, TResult> BuildCompositeFunc<TInstance, TResult>(this Type propertySetType,
                                                                                      Func<PropertyInfo, ParameterExpression, Expression> operationExpressionFactory,
                                                                                      Func<Expression, Expression, Expression> conjunction,
                                                                                      Expression seed,
                                                                                      Func<PropertyInfo, bool> propertyFilter = null)
        {
            // if no filter is specified, use all properties
            propertyFilter = propertyFilter ?? (_ => true);

            // we are building a function of the form
            // TResult <anonymous>(TInstance _instance)
            // {
            //      return seed 
            //      <conjunction> <op expr>(_instance.<property 1>);
            //      <conjunction> <op expr>(_instance.<property 2>);
            //      ...
            //      <conjunction> <op expr>(_instance.<property N>);
            // }

            // create the [_instance] parameter
            var instanceParameterExpression = Expression.Parameter(typeof (TInstance),
                                                                   "_instance");

            // get the set of operation expressions for the set of properties
            var expressions = propertySetType.GetOperationExpressions(propertyFilter,
                                                                      operationExpressionFactory,
                                                                      instanceParameterExpression);

            // join each of the expressions into a single expression using the conjunctionExpression, starting with the seed value.
            var conjoinedExpression = expressions.Aggregate(seed,
                                                            conjunction);

            // create a lambda expression, wiring up the _instance parameter
            var lambdaExpression = Expression.Lambda<Func<TInstance, TResult>>(conjoinedExpression,
                                                                               instanceParameterExpression);

            // Voila!
            return lambdaExpression.Compile();
        }

  
#endif

#if OLD

    ///<summary>
    ///  Use this function when you want to reflect over all the properties over a type and perform some action each property on two instances of that type.
    ///</summary>
    ///<typeparam name="TLeft"> The (interface) type of the first (left-hand-side) object being operated upon. Typically, <paramref
    ///   name="propertySetType" /> and <typeparamref name="TLeft" /> are isomorphic types, if not the same type </typeparam>
    ///<typeparam name="TRight"> The (interface) type of the second (right-hand-side) object being operated upon. Typically, <paramref
    ///   name="propertySetType" /> and <typeparamref name="TRight" /> are isomorphic types, if not the same type </typeparam>
    ///<param name="propertySetType"> The type of the object on which to reflect properties </param>
    ///<param name="operationExpressionFactory"> A factory delegate which should return the appropriate expression for a given property with reference to the parameter </param>
    ///<param name="propertyFilter"> An optional filter to define a subset of properties to work on </param>
    ///<returns> A method which takes two arguments of type <typeparamref name="TLeft" /> and <typeparamref name="TRight" /> and performs the specified operation on each property over the two arguments </returns>
    ///<example>
    ///  Assignment: For code which copies an instance of a given type into another instance one property at a time, we might want to write code as follows:
    /// <code>
    /// bool EqualProperties(Foo left, Foo right)
    /// {
    ///     left.Height = right.Height;
    ///     left.Width = right.Width;
    ///     ...
    /// }
    /// </code>
    /// This is obviously onerous, because one has to write out all the properties by hand.
    /// Furthermore, it requires maintenance when the list of properties defined on Foo changes. 
    /// Instead, we could use this builder to generate us exactly such a function as follows: 
    /// <code>static class Copier{T}
    /// {
    ///     private static readonly Action{T, T} _copy = typeof (T).BuildCompositeAction{T, T}(
    ///         (_pi, _l, _r) => Expression.Assign(Expression.Property(_l, _pi), Expression.Property(_r, _pi)));
    ///     
    ///     public static bool Copy(T left, T right)
    ///     {
    ///         return _copy(left, right);
    ///     }
    /// }
    /// </code> 
    /// This dynamically generates the operation lambdas just once at the static initializer time, 
    /// and allows the compiler to memoize the functions based on the generic type argument.
    /// Consequently there is very little loss of runtime performance when Copy() is called many times repetitively.
    ///</example>
        public static Action<TLeft, TRight> BuildCompositeAction<TLeft, TRight>(this Type propertySetType,
                                                                                Func<PropertyInfo, ParameterExpression, ParameterExpression, Expression> operationExpressionFactory,
                                                                                Func<PropertyInfo, bool> propertyFilter = null)
        {
            // if no filter is specified, use all properties
            propertyFilter = propertyFilter ?? (_ => true);

            // we are building a function of the form
            // TResult <anonymous>(TLeft _left, TRight _right)
            // {
            //      return seed 
            //      <conjunction> <op expr>(_left.<property 1>, _right.<property 1>);
            //      <conjunction> <op expr>(_left.<property 2>, _right.<property 2>);
            //      ...
            //      <conjunction> <op expr>(_left.<property N>, _right.<property N>);
            // }

            // create the [_left] parameter
            var leftParameterExpression = Expression.Parameter(typeof(TLeft),
                                                               "_left");
            // create the [_right] parameter
            var rightParameterExpression = Expression.Parameter(typeof(TRight),
                                                                "_right");

            // get the set of operation expressions for the set of properties
            var expressions = propertySetType.GetExpressions(propertyFilter,
                                                             operationExpressionFactory,
                                                             leftParameterExpression,
                                                             rightParameterExpression);

            // create a block expression surrounding all these expressions
            var blockExpression = Expression.Block(expressions);

            // create a lambda expression, wiring up the _left and _right parameters
            var lambdaExpression = Expression.Lambda<Action<TLeft, TRight>>(blockExpression,
                                                                            leftParameterExpression,
                                                                            rightParameterExpression);

            // Voila!
            return lambdaExpression.Compile();
        }

        
#endif

#if OLD

    ///<summary>
    ///  Use this function when you want to reflect over all the properties over a type, compute some result for each property of two instances of that type, and aggregate those results into a single value.
    ///</summary>
    ///<typeparam name="TLeft"> The (interface) type of the first (left-hand-side) object being operated upon. Typically, <paramref
    ///   name="propertySetType" /> and <typeparamref name="TLeft" /> are isomorphic types, if not the same type </typeparam>
    ///<typeparam name="TRight"> The (interface) type of the second (right-hand-side) object being operated upon. Typically, <paramref
    ///   name="propertySetType" /> and <typeparamref name="TRight" /> are isomorphic types, if not the same type </typeparam>
    ///<typeparam name="TResult"> The type of the result of the generated function, which is also the type of the result of the individual lambda expressions inside the generated function </typeparam>
    ///<param name="propertySetType"> The type of the object on which to reflect properties </param>
    ///<param name="operationExpressionFactory"> A factory delegate which should return the appropriate expression for a given property with reference to the parameter </param>
    ///<param name="conjunction"> A function which folds the set of results of the individual lambda expressions inside the generated function into a single value </param>
    ///<param name="seed"> The initial (default) result value of the fold </param>
    ///<param name="propertyFilter"> An optional filter to define a subset of properties to work on </param>
    ///<returns> A function which takes two arguments of type <typeparamref name="TLeft" /> and <typeparamref name="TRight" /> , computes a value for each property using the specified operation, and folds those values into a single result of <typeparamref
    ///   name="TResult" /> </returns>
    ///<example>
    ///  Comparision: For code which compares two instances of a given type by comparing each property, we might want to write code as follows 
    /// <code>
    /// bool EqualProperties(Foo left, Foo right)
    /// {
    ///     return 
    ///         (left.Height == right.Height) &amp;&amp; 
    ///         (left.Width == right.Width) &amp;&amp;
    ///         ...
    ///     ;
    /// }
    /// </code>
    /// This is obviously onerous, because one has to write out all the properties by hand.
    /// Furthermore, it requires maintenance when the list of properties defined on Foo changes.
    /// Instead, we could use this builder to generate us exactly such a function as follows: 
    /// <code>
    /// static class Comparer{T}
    /// {
    ///     private static readonly Func{T, T, bool} _compare = typeof (T).BuildCompositeFunc{T, T, bool}(
    ///         (_pi, _l, _r) => Expression.Equal(Expression.Property(_l, _pi), Expression.Property(_r, _pi)),
    ///             Expression.AndAlso,
    ///             Expression.Constant(true));
    ///
    ///     public static bool Compare(T left, T right)
    ///     {
    ///         return _compare(left, right);
    ///     }
    /// }</code> 
    /// This would generate code which looked like 
    /// <code>
    ///     return true &amp;&amp;
    ///         (left.Height == right.Height) &amp;&amp;
    ///         (left.Width == right.Width) &amp;&amp;
    ///         ...
    ///         ;
    /// </code>
    /// This dynamically generates the operation lambdas just once at the static initializer time,
    /// and allows the compiler to memoize the functions based on the generic type argument.
    /// Consequently there is very little loss of runtime performance when Compare() is called many times repetitively.
    ///</example>
        public static Func<TLeft, TRight, TResult> BuildCompositeFunc<TLeft, TRight, TResult>(this Type propertySetType,
                                                                                              Func<PropertyInfo, ParameterExpression, ParameterExpression, Expression> operationExpressionFactory,
                                                                                              Func<Expression, Expression, Expression> conjunction,
                                                                                              Expression seed,
                                                                                              Func<PropertyInfo, bool> propertyFilter = null)
        {
            // if no filter is specified, use all properties
            propertyFilter = propertyFilter ?? (_ => true);

            // we are building a function of the form
            // TResult <anonymous>(TLeft _left, TRight _right)
            // {
            //      return seed 
            //      <conjunction> <op expr>(_left.<property 1>, _right.<property 1>);
            //      <conjunction> <op expr>(_left.<property 2>, _right.<property 2>);
            //      ...
            //      <conjunction> <op expr>(_left.<property N>, _right.<property N>);
            // }

            // create the [_left] parameter
            var leftParameterExpression = Expression.Parameter(typeof(TLeft),
                                                               "_left");
            // create the [_right] parameter
            var rightParameterExpression = Expression.Parameter(typeof(TRight),
                                                                "_right");

            // get the set of operation expressions for the set of properties
            var expressions = propertySetType.GetExpressions(propertyFilter,
                                                             operationExpressionFactory,
                                                             leftParameterExpression,
                                                             rightParameterExpression);

            // join each of the expressions into a single expression using the conjunctionExpression, starting with the seed value.
            var conjoinedExpression = expressions.Aggregate(seed,
                                                            conjunction);

            // create a lambda expression, wiring up the _left and _right parameters
            var lambdaExpression = Expression.Lambda<Func<TLeft, TRight, TResult>>(conjoinedExpression,
                                                                                   leftParameterExpression,
                                                                                   rightParameterExpression);

            // Voila!
            return lambdaExpression.Compile();
        }
        
#endif

#if OLD

        private static IEnumerable<Expression> GetOperationExpressions(this Type propertySetType,
                                                                       Func<PropertyInfo, bool> propertyFilter,
                                                                       Func<PropertyInfo, ParameterExpression, Expression> operationExpressionFactory,
                                                                       ParameterExpression instanceParameterExpression)
        {
            // get a list of the properties
            var properties = propertySetType.GetAllProperties()
                                            .Where(propertyFilter);

            // for each of the properties, safely generate an expression referencing the property and the _instance parameter.
            return properties.Select(_ =>
                                     {
                                         try
                                         {
                                             return operationExpressionFactory(_,
                                                                               instanceParameterExpression);
                                         }
                                         catch
                                         {
                                             return Expression.Default(typeof(void));
                                         }
                                     });
        }

        private static IEnumerable<Expression> GetExpressions(this Type propertySetType,
                                                              Func<PropertyInfo, bool> propertyFilter,
                                                              Func<PropertyInfo, ParameterExpression, ParameterExpression, Expression> operationExpressionFactory,
                                                              ParameterExpression leftParameterExpression,
                                                              ParameterExpression rightParameterExpression)
        {
            var properties = propertySetType.GetAllProperties()
                                            .Where(propertyFilter);

            // for each of the properties, safely generate an expression referencing the property and the _instance parameter.
            return properties.Select(_ =>
                                     {
                                         try
                                         {
                                             return operationExpressionFactory(_,
                                                                               leftParameterExpression,
                                                                               rightParameterExpression);
                                         }
                                         catch
                                         {
                                             return Expression.Default(typeof(void));
                                         }
                                     });
        }
        
#endif
    }
}